#pragma once
#include <string>
#include "Mmdeviceapi.h"
#include "Functiondiscoverykeys_devpkey.h"

#if 1

typedef  void(*RegisterCallback)(const char* msg, int len, int info);

#define EXIT_ON_ERROR(hres)  \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
if ((punk) != NULL)  \
{ (punk)->Release(); (punk) = NULL; }
#define SAFE_RELEASE(punk)  \
if ((punk) != NULL)  \
{ (punk)->Release(); (punk) = NULL; }

class CMMNotificationClient : public IMMNotificationClient
{
	RegisterCallback p_RegisterCallback;
	char* DeviceName;
	int Notice;
	LONG _cRef;
	IMMDeviceEnumerator *_pEnumerator;

public:
	CMMNotificationClient() :
		_cRef(1),
		_pEnumerator(NULL)
	{
	}

	~CMMNotificationClient()
	{
		SAFE_RELEASE(_pEnumerator)
	}

	void STDMETHODCALLTYPE RegisterCallbackFun(RegisterCallback _RegisterCallback)
	{
		p_RegisterCallback = _RegisterCallback;
	}
	// IUnknown methods -- AddRef, Release, and QueryInterface

	ULONG STDMETHODCALLTYPE AddRef()
	{
		return InterlockedIncrement(&_cRef);
	}

	ULONG STDMETHODCALLTYPE Release()
	{
		ULONG ulRef = InterlockedDecrement(&_cRef);
		if (0 == ulRef)
		{
			delete this;
		}
		return ulRef;
	}

	HRESULT STDMETHODCALLTYPE QueryInterface(
		REFIID riid, VOID **ppvInterface)
	{
		if (IID_IUnknown == riid)
		{
			AddRef();
			*ppvInterface = (IUnknown*)this;
		}
		else if (__uuidof(IMMNotificationClient) == riid)
		{
			AddRef();
			*ppvInterface = (IMMNotificationClient*)this;
		}
		else
		{
			*ppvInterface = NULL;
			return E_NOINTERFACE;
		}
		return S_OK;
	}

	// Callback methods for device-event notifications.

	HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
		EDataFlow flow, ERole role,
		LPCWSTR pwstrDeviceId)
	{
		char  *pszFlow = "?????";
		char  *pszRole = "?????";


		switch (flow)
		{
		case eRender:
			pszFlow = "eRender";
			break;
		case eCapture:
			pszFlow = "eCapture";
			break;
		}

		switch (role)
		{
		case eConsole:
			pszRole = "eConsole";
			break;
		case eMultimedia:
		{
			pszRole = "eMultimedia";
			DeviceName = pszFlow;
			if (p_RegisterCallback != NULL)
			{
				p_RegisterCallback(DeviceName, strlen(DeviceName), Notice);
			}
			break;
		}
		case eCommunications:
			pszRole = "eCommunications";
			break;
		}

		printf("  -->New default device: flow = %s, role = %s\n",
			pszFlow, pszRole);
		return S_OK;
	}

	HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
	{
		printf("  -->Added device\n");
		return S_OK;
	};

	HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
	{

		printf("  -->Removed device\n");
		return S_OK;
	}

	HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
		LPCWSTR pwstrDeviceId,
		DWORD dwNewState)
	{
		char  *pszState = "?????";

		switch (dwNewState)
		{
		case DEVICE_STATE_ACTIVE:
		{
			Notice = 1;
			pszState = "ACTIVE";
			break;
		}
		case DEVICE_STATE_DISABLED:
		{
			Notice = 2;
			pszState = "DISABLED";
			break;
		}
		case DEVICE_STATE_NOTPRESENT:
		{
			Notice = 4;
			pszState = "NOTPRESENT";
			break;
		}
		case DEVICE_STATE_UNPLUGGED:
		{
			Notice = 8;
			pszState = "UNPLUGGED";
			break;
		}
		}

		printf("  -->New device state is DEVICE_STATE_%s (0x%8.8x)\n",
			pszState, dwNewState);

		return S_OK;
	}

	HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
		LPCWSTR pwstrDeviceId,
		const PROPERTYKEY key)
	{
		return S_OK;
	}
};
#endif
